cargo check
authorNick Cameron <ncameron@mozilla.com>
Wed, 16 Nov 2016 01:46:24 +0000 (14:46 +1300)
committerNick Cameron <ncameron@mozilla.com>
Fri, 9 Dec 2016 19:00:07 +0000 (09:00 -1000)
Adds a new mode - check - which only checks code, rather than generating machine code. It takes into account that proc macros and build scripts will still require generated code.

Implemented by adding a check profile and setting this on each Unit, unless the unit is required to be built (i.e., is a proc macro, build script, or dep of one).

12 files changed:
src/bin/cargo.rs
src/bin/check.rs [new file with mode: 0644]
src/cargo/core/manifest.rs
src/cargo/core/workspace.rs
src/cargo/ops/cargo_clean.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/toml.rs
src/etc/_cargo
src/etc/cargo.bashcomp.sh
src/etc/man/cargo-check.1 [new file with mode: 0644]

index 56a03a01011db1f4e824259497da68246df7faef..05cf786dd9c00b7698ded3063c3ce3c291bb3e3c 100644 (file)
@@ -51,6 +51,7 @@ Options:
 
 Some common cargo commands are (see all commands with --list):
     build       Compile the current project
+    check       Analyze the current project and report errors, but don't build object files
     clean       Remove the target directory
     doc         Build this project's and its dependencies' documentation
     new         Create a new cargo project
@@ -75,6 +76,7 @@ macro_rules! each_subcommand{
     ($mac:ident) => {
         $mac!(bench);
         $mac!(build);
+        $mac!(check);
         $mac!(clean);
         $mac!(doc);
         $mac!(fetch);
diff --git a/src/bin/check.rs b/src/bin/check.rs
new file mode 100644 (file)
index 0000000..4e36b29
--- /dev/null
@@ -0,0 +1,103 @@
+use std::env;
+
+use cargo::core::Workspace;
+use cargo::ops::{self, CompileOptions, MessageFormat};
+use cargo::util::important_paths::{find_root_manifest_for_wd};
+use cargo::util::{CliResult, Config};
+
+#[derive(RustcDecodable)]
+pub struct Options {
+    flag_package: Vec<String>,
+    flag_jobs: Option<u32>,
+    flag_features: Vec<String>,
+    flag_all_features: bool,
+    flag_no_default_features: bool,
+    flag_target: Option<String>,
+    flag_manifest_path: Option<String>,
+    flag_verbose: u32,
+    flag_quiet: Option<bool>,
+    flag_color: Option<String>,
+    flag_message_format: MessageFormat,
+    flag_release: bool,
+    flag_lib: bool,
+    flag_bin: Vec<String>,
+    flag_example: Vec<String>,
+    flag_test: Vec<String>,
+    flag_bench: Vec<String>,
+    flag_locked: bool,
+    flag_frozen: bool,
+}
+
+pub const USAGE: &'static str = "
+Check a local package and all of its dependencies for errors
+
+Usage:
+    cargo check [options]
+
+Options:
+    -h, --help                   Print this message
+    -p SPEC, --package SPEC ...  Package to check
+    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
+    --lib                        Check only this package's library
+    --bin NAME                   Check only the specified binary
+    --example NAME               Check only the specified example
+    --test NAME                  Check only the specified test target
+    --bench NAME                 Check only the specified benchmark target
+    --release                    Check artifacts in release mode, with optimizations
+    --features FEATURES          Space-separated list of features to also check
+    --all-features               Check all available features
+    --no-default-features        Do not check the `default` feature
+    --target TRIPLE              Check for the target triple
+    --manifest-path PATH         Path to the manifest to compile
+    -v, --verbose ...            Use verbose output
+    -q, --quiet                  No output printed to stdout
+    --color WHEN                 Coloring: auto, always, never
+    --message-format FMT         Error format: human, json [default: human]
+    --frozen                     Require Cargo.lock and cache are up to date
+    --locked                     Require Cargo.lock is up to date
+
+If the --package argument is given, then SPEC is a package id specification
+which indicates which package should be built. If it is not given, then the
+current package is built. For more information on SPEC and its format, see the
+`cargo help pkgid` command.
+
+Compilation can be configured via the use of profiles which are configured in
+the manifest. The default profile for this command is `dev`, but passing
+the --release flag will use the `release` profile instead.
+";
+
+pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
+    debug!("executing; cmd=cargo-check; args={:?}",
+           env::args().collect::<Vec<_>>());
+    config.configure(options.flag_verbose,
+                     options.flag_quiet,
+                     &options.flag_color,
+                     options.flag_frozen,
+                     options.flag_locked)?;
+
+    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
+
+    let opts = CompileOptions {
+        config: config,
+        jobs: options.flag_jobs,
+        target: options.flag_target.as_ref().map(|t| &t[..]),
+        features: &options.flag_features,
+        all_features: options.flag_all_features,
+        no_default_features: options.flag_no_default_features,
+        spec: &options.flag_package,
+        mode: ops::CompileMode::Check,
+        release: options.flag_release,
+        filter: ops::CompileFilter::new(options.flag_lib,
+                                        &options.flag_bin,
+                                        &options.flag_test,
+                                        &options.flag_example,
+                                        &options.flag_bench),
+        message_format: options.flag_message_format,
+        target_rustdoc_args: None,
+        target_rustc_args: None,
+    };
+
+    let ws = Workspace::new(&root, config)?;
+    ops::compile(&ws, &opts)?;
+    Ok(None)
+}
index da40c6a55a0b79366eda828a174d7ef08dea4db8..d5a2f25e0f546e5a1a0124068f84ee3e5296ef97 100644 (file)
@@ -71,7 +71,7 @@ impl LibKind {
             "lib" => LibKind::Lib,
             "rlib" => LibKind::Rlib,
             "dylib" => LibKind::Dylib,
-            "procc-macro" => LibKind::ProcMacro,
+            "proc-macro" => LibKind::ProcMacro,
             s => LibKind::Other(s.to_string()),
         }
     }
@@ -136,6 +136,7 @@ pub struct Profile {
     pub test: bool,
     pub doc: bool,
     pub run_custom_build: bool,
+    pub check: bool,
     pub panic: Option<String>,
 }
 
@@ -168,6 +169,7 @@ pub struct Profiles {
     pub bench_deps: Profile,
     pub doc: Profile,
     pub custom_build: Profile,
+    pub check: Profile,
 }
 
 /// Information about a binary, a library, an example, etc. that is part of the
@@ -531,6 +533,13 @@ impl Profile {
             ..Profile::default_dev()
         }
     }
+
+    pub fn default_check() -> Profile {
+        Profile {
+            check: true,
+            ..Profile::default_dev()
+        }
+    }
 }
 
 impl Default for Profile {
@@ -547,6 +556,7 @@ impl Default for Profile {
             test: false,
             doc: false,
             run_custom_build: false,
+            check: false,
             panic: None,
         }
     }
@@ -560,6 +570,8 @@ impl fmt::Display for Profile {
             write!(f, "Profile(doc)")
         } else if self.run_custom_build {
             write!(f, "Profile(run)")
+        } else if self.check {
+            write!(f, "Profile(check)")
         } else {
             write!(f, "Profile(build)")
         }
index f35432f646986c581c6635099319b5659d053c2f..d0b0fdeec15c59f32052b0278d57b10936a6b971 100644 (file)
@@ -450,6 +450,7 @@ impl<'cfg> Workspace<'cfg> {
                 bench_deps: Profile::default_release(),
                 doc: Profile::default_doc(),
                 custom_build: Profile::default_custom_build(),
+                check: Profile::default_check(),
             };
 
             for pkg in self.members().filter(|p| p.manifest_path() != root_manifest) {
index fd270b79aba5e2f573a07f703bc3e0d48adcf3b0..330d396dff52fd1016994b4cc8122f9e18bfcd0b 100644 (file)
@@ -54,10 +54,10 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
             for kind in [Kind::Host, Kind::Target].iter() {
                 let Profiles {
                     ref release, ref dev, ref test, ref bench, ref doc,
-                    ref custom_build, ref test_deps, ref bench_deps,
+                    ref custom_build, ref test_deps, ref bench_deps, ref check
                 } = *profiles;
                 let profiles = [release, dev, test, bench, doc, custom_build,
-                                test_deps, bench_deps];
+                                test_deps, bench_deps, check];
                 for profile in profiles.iter() {
                     units.push(Unit {
                         pkg: &pkg,
index e3b22f9d8e31511c336972577d640abe6a7bd233..da6f538ce61b018fdf6b7361535945e912fa65c0 100644 (file)
@@ -69,6 +69,7 @@ pub struct CompileOptions<'a> {
 pub enum CompileMode {
     Test,
     Build,
+    Check,
     Bench,
     Doc { deps: bool },
 }
@@ -188,13 +189,13 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
 
     let profiles = ws.profiles();
 
-    let resolve = resolve_dependencies(ws,
-                                       source,
-                                       features,
-                                       all_features,
-                                       no_default_features,
-                                       &spec)?;
-    let (spec, packages, resolve_with_overrides) = resolve;
+    let pair = resolve_dependencies(ws,
+                                    source,
+                                    features,
+                                    all_features,
+                                    no_default_features,
+                                    &specs)?;
+    let (packages, resolve_with_overrides) = pair;
 
     let mut pkgids = Vec::new();
     if spec.len() > 0 {
@@ -335,6 +336,7 @@ fn generate_targets<'a>(pkg: &'a Package,
         CompileMode::Test => test,
         CompileMode::Bench => &profiles.bench,
         CompileMode::Build => build,
+        CompileMode::Check => &profiles.check,
         CompileMode::Doc { .. } => &profiles.doc,
     };
     match *filter {
@@ -366,7 +368,7 @@ fn generate_targets<'a>(pkg: &'a Package,
                     }
                     Ok(base)
                 }
-                CompileMode::Build => {
+                CompileMode::Build | CompileMode::Check => {
                     Ok(pkg.targets().iter().filter(|t| {
                         t.is_bin() || t.is_lib()
                     }).map(|t| (t, profile)).collect())
index e0759ad81043cacc23e787a4ec820a7016b732d2..bb8176a6f0559b2973a751fdc6cb4a4bc27a5b28 100644 (file)
@@ -20,7 +20,7 @@ use super::layout::Layout;
 use super::links::Links;
 use super::{Kind, Compilation, BuildConfig};
 
-#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
 pub struct Unit<'a> {
     pub pkg: &'a Package,
     pub target: &'a Target,
@@ -70,9 +70,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         let dest = if build_config.release { "release" } else { "debug" };
         let host_layout = Layout::new(ws, None, &dest)?;
         let target_layout = match build_config.requested_target.as_ref() {
-            Some(target) => {
-                Some(Layout::new(ws, Some(&target), &dest)?)
-            }
+            Some(target) => Some(Layout::new(ws, Some(&target), dest)?),
             None => None,
         };
 
@@ -148,6 +146,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                         unit: &Unit<'a>,
                         crate_types: &mut BTreeSet<String>)
                         -> CargoResult<()> {
+        if unit.profile.check {
+            crate_types.insert("metadata".to_string());
+        }
         for target in unit.pkg.manifest().targets() {
             crate_types.extend(target.rustc_crate_types().iter().map(|s| {
                 if *s == "lib" {
@@ -207,6 +208,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                     line.contains(crate_type)
             });
             if not_supported {
+                if crate_type == "metadata" {
+                    bail!("compiler does not support `--crate-type metadata`, \
+                           cannot run `cargo check`.");
+                }
                 map.insert(crate_type.to_string(), None);
                 continue
             }
@@ -251,8 +256,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         let mut visited = HashSet::new();
         for unit in units {
             self.walk_used_in_plugin_map(unit,
-                                              unit.target.for_host(),
-                                              &mut visited)?;
+                                         unit.target.for_host(),
+                                         &mut visited)?;
         }
         Ok(())
     }
@@ -270,8 +275,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         }
         for unit in self.dep_targets(unit)? {
             self.walk_used_in_plugin_map(&unit,
-                                              is_plugin || unit.target.for_host(),
-                                              visited)?;
+                                         is_plugin || unit.target.for_host(),
+                                         visited)?;
         }
         Ok(())
     }
@@ -509,20 +514,25 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                     }
                 }
             };
-            match *unit.target.kind() {
-                TargetKind::Example |
-                TargetKind::Bin |
-                TargetKind::CustomBuild |
-                TargetKind::Bench |
-                TargetKind::Test => {
-                    add("bin", false)?;
-                }
-                TargetKind::Lib(..) if unit.profile.test => {
-                    add("bin", false)?;
-                }
-                TargetKind::Lib(ref libs) => {
-                    for lib in libs {
-                        add(lib.crate_type(), lib.linkable())?;
+
+            if unit.profile.check {
+                add("metadata", true)?;
+            } else {
+                match *unit.target.kind() {
+                    TargetKind::Example |
+                    TargetKind::Bin |
+                    TargetKind::CustomBuild |
+                    TargetKind::Bench |
+                    TargetKind::Test => {
+                        add("bin", false)?;
+                    }
+                    TargetKind::Lib(..) if unit.profile.test => {
+                        add("bin", false)?;
+                    }
+                    TargetKind::Lib(ref libs) => {
+                        for lib in libs {
+                            add(lib.crate_type(), lib.linkable())?;
+                        }
                     }
                 }
             }
@@ -593,12 +603,20 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             match self.get_package(id) {
                 Ok(pkg) => {
                     pkg.targets().iter().find(|t| t.is_lib()).map(|t| {
-                        Ok(Unit {
+                        let profile = if unit.profile.check &&
+                                         !t.is_custom_build()
+                                         && !t.for_host() {
+                            &self.profiles.check
+                        } else {
+                            self.lib_profile()
+                        };
+                        let unit = Unit {
                             pkg: pkg,
                             target: t,
-                            profile: self.lib_profile(),
+                            profile: profile,
                             kind: unit.kind.for_target(t),
-                        })
+                        };
+                        Ok(unit)
                     })
                 }
                 Err(e) => Some(Err(e))
index 809437c644abd9aa0dba631b8dd9f32f706c9435..6c59910537cbc913a98b211bef53a41e655885e1 100644 (file)
@@ -52,7 +52,7 @@ pub struct TargetConfig {
     pub overrides: HashMap<String, BuildOutput>,
 }
 
-pub type PackagesToBuild<'a> = [(&'a Package, Vec<(&'a Target,&'a Profile)>)];
+pub type PackagesToBuild<'a> = [(&'a Package, Vec<(&'a Target, &'a Profile)>)];
 
 // Returns a mapping of the root package plus its immediate dependencies to
 // where the compiled libraries are all located.
@@ -203,6 +203,7 @@ fn compile<'a, 'cfg: 'a>(cx: &mut Context<'a, 'cfg>,
     for unit in cx.dep_targets(unit)?.iter() {
         compile(cx, jobs, unit)?;
     }
+
     Ok(())
 }
 
@@ -270,11 +271,20 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
 
         // FIXME(rust-lang/rust#18913): we probably shouldn't have to do
         //                              this manually
-        for &(ref dst, ref _link_dst, _linkable) in filenames.iter() {
-            if fs::metadata(&dst).is_ok() {
-                fs::remove_file(&dst).chain_error(|| {
-                    human(format!("Could not remove file: {}.", dst.display()))
-                })?;
+        for &(ref filename, ref _link_dst, _linkable) in filenames.iter() {
+            let mut dsts = vec![root.join(filename)];
+            // If there is both an rmeta and rlib, rustc will prefer to use the
+            // rlib, even if it is older. Therefore, we must delete the rlib to
+            // force using the new rmeta.
+            if dsts[0].extension() == Some(&OsStr::new("rmeta")) {
+                dsts.push(root.join(filename).with_extension("rlib"));
+            }
+            for dst in &dsts {
+                if fs::metadata(dst).is_ok() {
+                    fs::remove_file(dst).chain_error(|| {
+                        human(format!("Could not remove file: {}.", dst.display()))
+                    })?;
+                }
             }
         }
 
@@ -528,7 +538,7 @@ fn build_base_args(cx: &mut Context,
     let Profile {
         ref opt_level, lto, codegen_units, ref rustc_args, debuginfo,
         debug_assertions, rpath, test, doc: _doc, run_custom_build,
-        ref panic, rustdoc_args: _,
+        ref panic, rustdoc_args: _, check,
     } = *unit.profile;
     assert!(!run_custom_build);
 
@@ -548,7 +558,9 @@ fn build_base_args(cx: &mut Context,
         cmd.arg("--error-format").arg("json");
     }
 
-    if !test {
+    if check {
+        cmd.arg("--crate-type").arg("metadata");
+    } else if !test {
         for crate_type in crate_types.iter() {
             cmd.arg("--crate-type").arg(crate_type);
         }
index 18b4d4c6cfc0a1b109cd7cd2ae845f105c5d19b7..7d47bcf928ef9c85e1b851859015b54fbc1facad 100644 (file)
@@ -1248,6 +1248,8 @@ fn build_profiles(profiles: &Option<TomlProfiles>) -> Profiles {
         doc: merge(Profile::default_doc(),
                    profiles.and_then(|p| p.doc.as_ref())),
         custom_build: Profile::default_custom_build(),
+        check: merge(Profile::default_check(),
+                   profiles.and_then(|p| p.dev.as_ref())),
     };
     // The test/bench targets cannot have panic=abort because they'll all get
     // compiled with --test which requires the unwind runtime currently
@@ -1277,6 +1279,7 @@ fn build_profiles(profiles: &Option<TomlProfiles>) -> Profiles {
             test: profile.test,
             doc: profile.doc,
             run_custom_build: profile.run_custom_build,
+            check: profile.check,
             panic: panic.clone().or(profile.panic),
         }
     }
index 17585920252a7919d87a35e18e579460b3f9490a..4a23bf35280d8439d6fbaff5bdb490fb27611281 100644 (file)
@@ -51,6 +51,23 @@ case $state in
                     '--color=:colorization option:(auto always never)' \
                     ;;
 
+            check)
+                _arguments \
+                    '--features=[space separated feature list]' \
+                    '--all-features[enable all available features]' \
+                    '(-h, --help)'{-h,--help}'[show help message]' \
+                    '(-j, --jobs)'{-j,--jobs}'[number of parallel jobs, defaults to # of CPUs]' \
+                    "${command_scope_spec[@]}" \
+                    '--manifest-path=[path to manifest]: :_files -/' \
+                    '--no-default-features[do not check the default features]' \
+                    '(-p,--package)'{-p=,--package=}'[package to check]:packages:_get_package_names' \
+                    '--release=[check in release mode]' \
+                    '--target=[target triple]' \
+                    '(-v, --verbose)'{-v,--verbose}'[use verbose output]' \
+                    '(-q, --quiet)'{-q,--quiet}'[no output printed to stdout]' \
+                    '--color=:colorization option:(auto always never)' \
+                    ;;
+
             clean)
                 _arguments \
                     '(-h, --help)'{-h,--help}'[show help message]' \
@@ -384,6 +401,7 @@ _cargo_cmds(){
 local -a commands;commands=(
 'bench:execute all benchmarks of a local package'
 'build:compile the current project'
+'check:check the current project without compiling'
 'clean:remove generated artifacts'
 'doc:build package documentation'
 'fetch:fetch package dependencies'
index 5c6675e90690266db58cdeda966b0de95c77ac4c..44b8998839e0fb5938e2d508987e7ebcbe228bdf 100644 (file)
@@ -24,6 +24,7 @@ _cargo()
        local opt___nocmd="$opt_common -V --version --list"
        local opt__bench="$opt_common $opt_pkg $opt_feat $opt_mani $opt_jobs --target --lib --bin --test --bench --example --no-run"
        local opt__build="$opt_common $opt_pkg $opt_feat $opt_mani $opt_jobs --target --lib --bin --test --bench --example --release"
+    local opt__check="$opt_common $opt_pkg $opt_feat $opt_mani $opt_jobs --target --lib --bin --example"
        local opt__clean="$opt_common $opt_pkg $opt_mani --target --release"
        local opt__doc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_jobs --target --open --no-deps --release"
        local opt__fetch="$opt_common $opt_mani"
diff --git a/src/etc/man/cargo-check.1 b/src/etc/man/cargo-check.1
new file mode 100644 (file)
index 0000000..0931bf0
--- /dev/null
@@ -0,0 +1,132 @@
+.TH "CARGO\-CHECK" "1" "May 2016" "The Rust package manager" "Cargo Manual"
+.hy
+.SH NAME
+.PP
+cargo\-check \- Check the current project
+.SH SYNOPSIS
+.PP
+\f[I]cargo check\f[] [OPTIONS]
+.SH DESCRIPTION
+.PP
+Check a local package and all of its dependencies.
+.PP
+If the \f[B]\-\-package\f[] argument is given, then \f[I]SPEC\f[] is a
+package id specification which indicates which package should be checked.
+If it is not given, then the current package is checked.
+For more information on \f[I]SPEC\f[] and its format, see the "cargo
+help pkgid" command.
+.PP
+Compilation can be configured via the use of profiles which are
+configured in the manifest.
+The default profile for this command is \f[I]dev\f[], but passing the
+\f[B]\-\-release\f[] flag will use the \f[I]release\f[] profile instead.
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Print this message.
+.RS
+.RE
+.TP
+.B \-p \f[I]SPEC\f[], \-\-package \f[I]SPEC ...\f[]
+Package to check.
+.RS
+.RE
+.TP
+.B \-j \f[I]IN\f[], \-\-jobs \f[I]IN\f[]
+Number of parallel jobs, defaults to # of CPUs.
+.RS
+.RE
+.TP
+.B \-\-lib
+Check only this package\[aq]s library.
+.RS
+.RE
+.TP
+.B \-\-bin \f[I]NAME\f[]
+Check only the specified binary.
+.RS
+.RE
+.TP
+.B \-\-example \f[I]NAME\f[]
+Check only the specified example.
+.RS
+.RE
+.TP
+.B \-\-test \f[I]NAME\f[]
+Check only the specified test target.
+.RS
+.RE
+.TP
+.B \-\-bench \f[I]NAME\f[]
+Check only the specified benchmark target.
+.RS
+.RE
+.TP
+.B \-\-release
+Check artifacts in release mode.
+.RS
+.RE
+.TP
+.B \-\-all\-features
+Check with all available features.
+.RS
+.RE
+.TP
+.B \-\-features \f[I]FEATURES\f[]
+Space\-separated list of features to also check.
+.RS
+.RE
+.TP
+.B \-\-no\-default\-features
+Do not check the \f[C]default\f[] feature.
+.RS
+.RE
+.TP
+.B \-\-target \f[I]TRIPLE\f[]
+Check for the target triple.
+.RS
+.RE
+.TP
+.B \-\-manifest\-path \f[I]PATH\f[]
+Path to the manifest to compile.
+.RS
+.RE
+.TP
+.B \-v, \-\-verbose
+Use verbose output.
+.RS
+.RE
+.TP
+.B \-q, \-\-quiet
+No output printed to stdout.
+.RS
+.RE
+.TP
+.B \-\-color \f[I]WHEN\f[]
+Coloring: auto, always, never.
+.RS
+.RE
+.SH EXAMPLES
+.PP
+Check a local package and all of its dependencies
+.IP
+.nf
+\f[C]
+$\ cargo\ check
+\f[]
+.fi
+.PP
+Check a package with optimizations
+.IP
+.nf
+\f[C]
+$\ cargo\ check\ \-\-release
+\f[]
+.fi
+.SH SEE ALSO
+.PP
+cargo(1)
+.SH COPYRIGHT
+.PP
+This work is dual\-licensed under Apache 2.0 and MIT terms.
+See \f[I]COPYRIGHT\f[] file in the cargo source distribution.